#coding:utf-8
# -*- coding: UTF-8 -*-
'''
更新至Python3
'''
import os
import sqlite3
import json
import tornado.escape
import tornado.ioloop
import tornado.web
from tornado.options import define, options, parse_command_line
from tornado.web import RequestHandler
from tornado import httpclient
from qdcommon import BaseHandler, outputRes, get_myargument
import zipfile
import requests
import time
import math
from concurrent.futures import ThreadPoolExecutor
from tornado.concurrent import run_on_executor
import functools
from threading import Lock
from utils.FtpUtil import FtpUtil
from utils.LoggerUtil import LoggerUtil, InitLog, logger
from conf import config as conf
lock = Lock()

define("port", default=conf.port, help="run on the given port", type=int)

if not os.path.exists(conf.UpdatePath):
    os.makedirs(conf.UpdatePath)

class MyZip:
    '''
        压缩、解压类
    '''
    def compress(self, sourcedir, targetfile, whiteList=[]):
        with zipfile.ZipFile(targetfile,'w') as target:
            for i in os.walk(sourcedir):
                filt = False
                for dirname in whiteList:
                    if i[0].find(dirname) == -1:
                        filt = True
                        break
                if filt: continue
                for n in i[2]:
                    target.write(''.join((i[0],'\\',n)), ''.join((i[0],'\\',n)).replace(sourcedir+'\\', ''))
                    # windows路径

    def uncompress(self, zip_src, dst_dir):
        issuccess = False
        try:
            zipfile_r = zipfile.is_zipfile(zip_src)
            if zipfile_r:
                fz = zipfile.ZipFile(zip_src, 'r')
                for file in fz.namelist():
                    fz.extract(file, dst_dir)
                    issuccess = True
            else:
                issuccess = False
        except:
            issuccess = False
        return issuccess

class VersionDB:
    '''
    id 唯一编号,自动生成
    updateBy 记录更新人
    updateDate 记录更新时间
    backup_file 备份文件的文件名
    memo   备注
    operation_type  操作类型
    '''
    def __init__(self):
        self.dbpath = conf.versiondb
        self.dbkeys2retkeys = {
        "id":"id", 
        "updateBy":"updateBy", 
        "updateDate":"updateDate", 
        "backup_file":"backup_file",
        "memo":"memo",
        "operation_type":"operation_type"
        }   #数据库中的字段名->返回给前端的字段名映射
        self.retkeys2dbkeys = {k:v for v,k in self.dbkeys2retkeys.items()}
        self.tablename = "UpdateHistory"
        self.conn = None
        self.cur = None
        self.isconnect = False
        self.ret = None
        self.addsql = "INSERT INTO %s (updateBy, updateDate,backup_file, memo, operation_type) VALUES (?, datetime('now', 'localtime'), ?, ?, ?)" % (self.tablename, )

    def connect(self):
        logger.info('databasepath={}'.format(self.dbpath))
        self.conn = sqlite3.connect(self.dbpath)
        self.cur = self.conn.cursor()
        self.isconnect = True

    def commitandclose(self):
        self.conn.commit()
        self.conn.close()
        self.isconnect = False

    def execute(self, sql, sqltuple=None, ret=False):
        if sqltuple is not None:
            self.cur.execute(sql, sqltuple)
        else:
            self.cur.execute(sql)
        if ret:
            return self.cur.fetchall()
        else:
            return None

    def CreateTable(self):
        self.connect()
        if self.cur and (self.isconnect == True):
            self.cur.execute('''CREATE TABLE IF NOT EXISTS UpdateHistory
                                (id INTEGER PRIMARY KEY AUTOINCREMENT unique,
                                 updateBy    VARCHAR(64),
                                 updateDate  TimeStamp NOT NULL DEFAULT (datetime('now','localtime')),
                                 backup_file VARCHAR(64),
                                 memo        TEXT,
                                 operation_type INTEGER)''')

        self.commitandclose()

    def additem(self, arg):
        self.cur.execute(self.addsql, arg)

    def deleteitemsbyid(self, args):
        sql = 'delete from %s where id=?' % self.tablename
        self.cur.executemany(sql, args)

    def multichangeitem(self, arg, idlist):
        sqlstr = ''
        for key in arg:
            sqlstr += '%s=%s,' % (key, arg[key])
        sqlstr = sqlstr[:-1]
        sql = 'update ' + self.tablename +' set '+ sqlstr +' where id in (' + ','.join(idlist) + ')'
        self.cur.execute(sql)

    def getcountfromtable(self, deleted_data=False):  #获取总共有几条数据
        if not deleted_data:
            sql = 'select Count(*) from %s where operation_type <> -1' % self.tablename
        else:
            sql = 'select Count(*) from %s where operation_type = -1' % self.tablename
        datalist = self.execute(sql, ret=True)
        return datalist[0][0]


    def finditembyqdsoft_paging(self, pageNo, itemNum, deleted_data=False, last_find=False):
        '''
        1.获取分组
        2.获取搜索关键字
        3.在1，2 获得的数据基础上分页
        :param pageNo:  页码
        :param itemNum: 每页多少条
        :param myClass: 分组
        :param deleted_data: 是否查找删除的数据
        :param last_find: 从后面查找数据
        :return:
        '''
        if last_find: OrderStr = 'ORDER BY updateDate DESC '
        else:  OrderStr = ''
        offsetNum = (pageNo - 1) * itemNum
        if not deleted_data:
            sql = 'select * from %s where operation_type <> -1 %s limit %d offset %d' % (self.tablename, OrderStr, itemNum, offsetNum)
        else:
            sql = 'select * from %s where operation_type = -1 %s limit %d offset %d' % (self.tablename, OrderStr, itemNum, offsetNum)
        datalist = self.execute(sql,  ret=True)
        reslist = []
        for item in datalist:
            resitem = {}
            for i in range(len(list(self.retkeys2dbkeys.keys()))):
                resitem[list(self.retkeys2dbkeys.keys())[i]] = item[i]
            reslist.append(resitem)
        return reslist

    def finditemById(self, id):
        sql = 'select * from %s where id=?' % (self.tablename)
        datalist = self.execute(sql, (id, ),  ret=True)
        reslist = []
        for item in datalist:
            resitem = {}
            for i in range(len(list(self.retkeys2dbkeys.keys()))):
                resitem[list(self.retkeys2dbkeys.keys())[i]] = item[i]
            reslist.append(resitem)
        return reslist

    def __del__(self):
        if self.isconnect:
            self.commitandclose()

class Version_Update(BaseHandler):
    executor = ThreadPoolExecutor(4)

    @tornado.gen.coroutine
    def post(self):
        global VersionServerState
        if VersionServerState['state'] == 1:
            outputRes(self, {'result':-1, 'error':VersionServerState['message']})
            return None
        if VersionServerState['state'] == 0:
            VersionServerState['state'] = 1
        pageSize, username, memo = get_myargument(self, pageSize=None, username=None, memo=None)
        assert username is not None, 'username is null'
        assert memo is not None, 'memo is null'
        assert not(username == ''), 'username is empty string'

        UpdateFunc = functools.partial(self.update, pageSize, username, memo)
        tornado.ioloop.IOLoop.instance().add_callback(UpdateFunc)

        outputRes(self, {'result': 1})

    @run_on_executor
    def update(self, pageSize, username, memo):
        if pageSize: pageSize = int(pageSize)
        global VersionServerState
        logger.info('compress')
        zipobj = MyZip()
        zipfilename = 'html_%s.zip' % (time.strftime("%Y-%m-%d_%H-%M-%S",
                                                     time.localtime(time.time())))
        VersionServerState['state'] = 1
        VersionServerState['message'] = '正在压缩测试包'
        zipobj.compress(conf.UpdatePath, conf.UpdatePath + '\\' + zipfilename, conf.whiteList)
        logger.info('ftp uploading --- %s' % (zipfilename,))
        VersionServerState['message'] = '正在传输至正式服务器'
        os.system('.\\dll\\curl.exe %s -T "%s"' % (conf.ftpurl, conf.updatefile,)) #  conf.UpdatePath+ '\\' + zipfilename
        logger.info('unzip --- %s' % (zipfilename,))
        VersionServerState['message'] = '调用远程解压服务'
        # r = requests.get(conf.upzipurl % (zipfilename,))
        # logger.info((r, conf.upzipurl % (zipfilename,)))
        # r = requests.get(cleanurl % (zipfilename,))
        lock.acquire()
        db = VersionDB()
        db.connect()
        sqlarg = (username, zipfilename, memo, 1)
        db.additem(sqlarg)
        db.commitandclose()
        lock.release()
        VersionServerState['state'] = 0
        VersionServerState['message'] = '完成'
        return {'result':1}
        # totalNum = int(db.getcountfromtable())
        # pageNum = int(math.ceil((totalNum) * 1.0 / pageSize))
        # pageNo = pageNum
        # datalist = db.finditembyqdsoft_paging(pageNo, pageSize)
        # db.commitandclose()
        # outputRes(self, {}, result=1, data=datalist, pages=pageNum, pageNo=pageNo, pageSize=pageSize,
        #           totalSize=totalNum)

class Version_DeleteItem(BaseHandler):
    def post(self):
        idlist, pageNo, pageSize, username = get_myargument(self, idlist='[]', pageNo=None,  pageSize=None, username=None)
        username is not None, 'username is null'
        if pageNo is None or pageSize is None:
            outputRes(self, {'result':-1, 'error':'pageNo or pageSize is null'})
            return None
        pageSize = int(pageSize)
        pageNo = int(pageNo)
        idlist = json.loads(idlist)
        lock.acquire()
        db = VersionDB()
        db.connect()
        idlist = [str(i) for i in idlist]
        db.multichangeitem({'operation_type':-1}, idlist)
        totalNum = db.getcountfromtable()
        totalNum = totalNum - len(idlist)
        pageNum = int(math.ceil((totalNum) * 1.0 / pageSize))
        if pageNo > pageNum:
            pageNo = pageNum
        datalist = db.finditembyqdsoft_paging(pageNo, pageSize)
        db.commitandclose()
        lock.release()
        outputRes(self, {}, result=1, data=datalist, pages=pageNum, pageNo=pageNo, pageSize=pageSize, totalSize=totalNum)

class Version_BackUp(BaseHandler):
    executor = ThreadPoolExecutor(4)

    @tornado.gen.coroutine
    def post(self):
        global VersionServerState
        if VersionServerState['state'] == 1:
            outputRes(self, {'result': -1, 'error': VersionServerState['message']})
            return None
        if VersionServerState['state'] == 0:
            VersionServerState['state'] = 1
        id, pageSize, username, memo= get_myargument(self, id=None, pageSize=None, username=None, memo=None)
        id is not None, 'id is null'
        pageSize is not None, 'pageSize is null'
        username is not None, 'username is null'
        assert not(username == ''), 'username is empty string'
        memo  is not None, 'memo is null'

        backupFunc = functools.partial(self.backup, id, pageSize, username, memo)
        tornado.ioloop.IOLoop.instance().add_callback(backupFunc)

        outputRes(self, {'result': 1})

    @run_on_executor
    def backup(self, id, pageSize, username, memo):
        global VersionServerState
        pageSize = int(pageSize)
        lock.acquire()
        db = VersionDB()
        db.connect()
        data = db.finditemById(id)
        db.commitandclose()
        lock.release()
        if data[0]['operation_type'] == -1:
            outputRes(self, {'result': -1, 'error': '该版本已被删除，不能用于还原'})
            return None
        try:
            zipfilename = data[0]['backup_file']
        except:
            VersionServerState['state'] = 0

        VersionServerState['state'] = 1
        VersionServerState['message'] = '正在传输至正式服务器'
        logger.info('ftp uploading --- %s' % (zipfilename,))
        os.system('.\\dll\\curl.exe %s -T "%s"' % (conf.ftpurl, conf.htmlpath + '\\' + zipfilename,))
        logger.info('unzip --- %s' % (zipfilename,))
        VersionServerState['message'] = '调用远程解压服务'
        r = requests.get(conf.upzipurl % (zipfilename,))
        logger.info((r, conf.upzipurl % (zipfilename,)))
        # r = requests.get(cleanurl % (zipfilename,))
        lock.acquire()
        db = VersionDB()
        db.connect()
        sqlarg = (username, zipfilename, memo, 0)
        db.additem(sqlarg)
        # totalNum = int(db.getcountfromtable())
        # pageNum = int(math.ceil((totalNum) * 1.0 / pageSize))
        # pageNo = pageNum
        # datalist = db.finditembyqdsoft_paging(pageNo, pageSize)
        db.commitandclose()

        VersionServerState['state'] = 0
        VersionServerState['message'] = '完成'
        # outputRes(self, {}, result=1, data=datalist, pages=pageNum, pageNo=pageNo, pageSize=pageSize,
        #           totalSize=totalNum)

class Version_FindItem(BaseHandler):
    def post(self):
        pageSize, username, pageNo, deleted, last_find = get_myargument(self, pageSize=None, username=None, pageNo=None, deleted=0, last_find=0)
        assert pageSize is not None, 'pageSize is null'
        assert username is not None, 'username is null'
        assert not(username == ''), 'username is empty string'
        assert pageNo is not None, 'pageNo is null'
        pageSize = int(pageSize)
        last_find = int(last_find)
        if deleted == 0: deleted = False
        elif deleted == 1 : deleted = True
        if last_find == 0: last_find = False
        elif last_find == 1 : last_find = True
        db = VersionDB()
        db.connect()

        totalNum = int(db.getcountfromtable(deleted))
        pageNum = int(math.ceil((totalNum) * 1.0 / pageSize))
        pageNo = int(pageNo)
        datalist = db.finditembyqdsoft_paging(pageNo, pageSize, deleted, last_find)
        db.conn.commit()
        db.conn.close()
        db.isconnect = False
        outputRes(self, {}, result=1, data=datalist, pages=pageNum, pageNo=pageNo, pageSize=pageSize,
                  totalSize=totalNum)

class Versioin_CheckState(BaseHandler):
    def post(self):
        pageSize, username, memo, pageNo, deleted = get_myargument(self, pageSize=None, username=None, memo=None,
                                                                   pageNo=None, deleted=0)
        global Versioin_CheckState
        if VersionServerState['state'] == 0:
            db = VersionDB()
            db.connect()

            pageSize = int(pageSize)
            totalNum = int(db.getcountfromtable(deleted))
            pageNum = int(math.ceil((totalNum) * 1.0 / pageSize))
            pageNo = int(pageNo)
            datalist = db.finditembyqdsoft_paging(pageNo, pageSize, deleted)
            db.commitandclose()
            outputRes(self, VersionServerState, result=1,data=datalist, pages=pageNum, pageNo=pageNo, pageSize=pageSize,
                  totalSize=totalNum)
            return None
        if VersionServerState['state'] == 1:
            outputRes(self, VersionServerState, result=1)
            return None

def main():
    db = VersionDB()
    db.CreateTable()
    del db
    application = tornado.web.Application([
        (r'/VersionInfo/DeleteItem/', Version_DeleteItem),
        (r'/VersionInfo/Update/', Version_Update),
        (r'/VersionInfo/BackUp/', Version_BackUp),
        (r'/VersionInfo/FindItem/', Version_FindItem),
        (r'/VersionInfo/CheckState/', Versioin_CheckState),
    ], debug=True,
        #autoreload=True,
        template_path=conf.dataDBPath,
        static_hash_cache=False)  # 增加代码有改动，服务器自动重启
    # template_path = 'templates'
    # static_path = 'static'
    tornado.options.parse_command_line()
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.instance().start()


if __name__ == '__main__':
    VersionServerState = {'state': 0, 'message': '完成'}
    InitLog(conf.log_path)
    main()